#include <unistd.h>
#include <stdio.h>
#include "itch41reader.h"
#include "messages.h"

using namespace openitch;

//#define DEBUG 
#ifdef DEBUG
#include <stdlib.h>
static inline void assert(bool test)
{
	if (!test)
	{
		helpers::stacktrace();
		exit(1);
	}
}
#else
static inline void assert(bool test) {}
#endif


static inline int64_t getlong(char const * const buf, int const offset) {
	return be64toh(*(int64_t*) (buf+offset*sizeof(char)));
}
static inline int32_t getint (char const * const buf, int const offset) {
	return be32toh(*(int32_t*) (buf+offset*sizeof(char)));
}
static inline int16_t getshort(char const * const buf, int const offset) {
	return be16toh(*(int16_t*) (buf+offset*sizeof(char)));
}
static inline int8_t getchar(char const * const buf, int const offset) {
	return (int8_t) *(buf+offset*sizeof(char));
}

void set(timestamp_t * pkt, char const * const buf)
{
	assert(pkt->hdr.msgtype == getchar(buf,0));
	pkt->hdr.time = getint(buf,1);
}

void set(sysevent_t * pkt, char const * const buf)
{
	assert(pkt->hdr.msgtype == getchar(buf,0));
	pkt->hdr.time = getint(buf,1);
	pkt->code = getchar(buf,5);
}

void set(stkdirectory_t * pkt, char const * const buf)
{
	assert(pkt->hdr.msgtype == getchar(buf,0));
	pkt->hdr.time = getint(buf,1);
	memcpy(pkt->stock,buf+5,sizeof(pkt->stock));
	pkt->mktcategory = getchar(buf,13);
	pkt->financialstatusindicator=getchar(buf,14);
	pkt->roundlotsize=getint(buf,15);
	pkt->roundlotsonly='Y'==getchar(buf,19);
}

void set(stktradingaction_t * pkt, char const * const buf)
{
	assert(pkt->hdr.msgtype == getchar(buf,0));
	pkt->hdr.time = getint(buf,1);
	memcpy(pkt->stock,buf+5,sizeof(pkt->stock));
	pkt->tradingstate = getchar(buf,13);
	pkt->reserved = getchar(buf,14);
	memcpy(pkt->reason,buf+15,sizeof(pkt->reason));
}

void set(regshorestriction_t * pkt, char const * const buf)
{
	assert(pkt->hdr.msgtype == getchar(buf,0));
	pkt->hdr.time = getint(buf,1);
	memcpy(pkt->stock,buf+5,sizeof(pkt->stock));
	pkt->regshoaction = getchar(buf,13);
}

void set(mktparticipantposition_t * pkt, char const * const buf)
{
	assert(pkt->hdr.msgtype == getchar(buf,0));
	pkt->hdr.time = getint(buf,1);
	memcpy(pkt->mpid,buf+5,sizeof(pkt->mpid));
	memcpy(pkt->stock,buf+9,sizeof(pkt->stock));
	pkt->primarymktmaker = getchar(buf,17);
	pkt->mktmakermode = getchar(buf,18);
	pkt->mktparticipantstate = getchar(buf,19);
}

void set(addorder_t * pkt, char const * const buf)
{
	assert(pkt->hdr.msgtype == getchar(buf,0));
	pkt->hdr.time = getint(buf,1);
	pkt->id = getlong(buf,5);
	pkt->buy = ('B' == getchar(buf,13));
	pkt->shares = getint(buf,14);
	memcpy(pkt->stock,buf+18,sizeof(pkt->stock));
	pkt->price = getint(buf,26);
}

void set(addordermpid_t * pkt, char const * const buf)
{
	assert(pkt->hdr.msgtype == getchar(buf,0));
	pkt->hdr.time = getint(buf,1);
	pkt->id = getlong(buf,5);
	pkt->buy = ('B' == getchar(buf,13));
	pkt->shares = getint(buf,14);
	memcpy(pkt->stock,buf+18,sizeof(pkt->stock));
	pkt->price = getint(buf,26);
	memcpy(pkt->mpid,buf+30,sizeof(pkt->mpid));
}

void set(orderexecuted_t * pkt, char const * const buf)
{
	assert(pkt->hdr.msgtype == getchar(buf,0));
	pkt->hdr.time = getint(buf,1);
	pkt->id = getlong(buf,5);
	pkt->shares = getint(buf,13);
	pkt->matchId = getlong(buf,17);
}

void set(orderexecutedwithprice_t * pkt, char const * const buf)
{
	assert(pkt->hdr.msgtype == getchar(buf,0));
	pkt->hdr.time = getint(buf,1);
	pkt->id = getlong(buf,5);
	pkt->shares = getint(buf,13);
	pkt->matchId = getlong(buf,17);
	pkt->printable = ('Y' == getchar(buf,25));
	pkt->price = getint(buf,26);
}

void set(orderreduce_t * pkt, char const * const buf)
{
	assert(pkt->hdr.msgtype == getchar(buf,0));
	pkt->hdr.time = getint(buf,1);
	pkt->id = getlong(buf,5);
	pkt->shares = getint(buf,13);
}

void set(orderdelete_t * pkt, char const * const buf)
{
	assert(pkt->hdr.msgtype == getchar(buf,0));
	pkt->hdr.time = getint(buf,1);
	pkt->id = getlong(buf,5);
}

void set(orderreplace_t * pkt, char const * const buf)
{
	assert(pkt->hdr.msgtype == getchar(buf,0));
	pkt->hdr.time = getint(buf,1);
	pkt->oldid = getlong(buf,5);
	pkt->newid = getlong(buf,13);
	pkt->shares = getint(buf,21);
	pkt->price = getint(buf,25);
}

void set(trade_t * pkt, char const * const buf)
{
	assert(pkt->hdr.msgtype == getchar(buf,0));
	pkt->hdr.time = getint(buf,1);
	pkt->id = getlong(buf,5);
	pkt->buy = getchar(buf,13);
	pkt->shares = getint(buf,14);
	memcpy(pkt->stock,buf+18,sizeof(pkt->stock));
	pkt->price = getint(buf,26);
	pkt->matchId = getlong(buf,30);
}

void set(crosstrade_t * pkt, char const * const buf)
{
	assert(pkt->hdr.msgtype == getchar(buf,0));
	pkt->hdr.time = getint(buf,1);
	pkt->shares = getlong(buf,5);
	memcpy(pkt->stock,buf+13,sizeof(pkt->stock));
	pkt->price = getint(buf,21);
	pkt->matchId = getlong(buf,25);
	pkt->crosstype=getchar(buf,33);
}

void set(brokentrade_t * pkt, char const * const buf)
{
	assert(pkt->hdr.msgtype == getchar(buf,0));
	pkt->hdr.time = getint(buf,1);
	pkt->matchId = getlong(buf,5);
}

void set(noii_t * pkt, char const * const buf)
{
	assert(pkt->hdr.msgtype == getchar(buf,0));
	pkt->hdr.time = getint(buf,1);
	pkt->pairedshares = getlong(buf,5);
	pkt->imbalanceshares = getlong(buf,13);
	pkt->imbalancedirection=getlong(buf,21);
	memcpy(pkt->stock,buf+22,sizeof(pkt->stock));
	pkt->farprice = getint(buf,30);
	pkt->nearprice = getint(buf,34);
	pkt->currentrefprice = getint(buf,38);
	pkt->crosstype = getchar(buf,42);
	pkt->pricevariationindicator = getchar(buf,43);
}

void set(rpii_t * pkt, char const * const buf)
{
	assert (pkt->hdr.msgtype == getchar(buf,0));
	pkt->hdr.time = getint(buf,1);
	memcpy(pkt->stock,buf+5,sizeof(pkt->stock));
	pkt->interestflag = getchar(buf,13);
}

// itch41
const bool usebuffer = true;
itch41reader::itch41reader(dataclient * dc, int bufsize) :
wrapper(dc), bufsize(bufsize)
{
	starttime = 0;
	bytesread = 0;
	msgcount = 0;
	buf = new char[bufsize];
	// machinery for poor mans buffered reader
	pktposition = 0;
	if (usebuffer)
		pktposition = bufsize;
	pktsize = 0;
}

itch41reader::~itch41reader()
{
	if (buf) delete buf;
}

char itch41reader::processpacket(int fd)
{
	//printf("start pktposition:%d,pktsize:%d\n",pktposition,pktsize);
	if (usebuffer)
	{
		if (pktposition >= bufsize-2)
			bufferedread(fd);
		pktsize = getshort(buf,pktposition);
		pktposition += 2;
		if (pktposition >= bufsize - pktsize)
			bufferedread(fd);
	}
	else
	{
		int bytes = read(fd,&pktsize,sizeof(pktsize));
		pktsize = be16toh(pktsize);
		bytes = read(fd,buf,pktsize);
	}
	char header = *(char*)(buf+pktposition);
	switch (header)
	{
	case 'T' :
	{
		timestamp_t pkt;
		set(&pkt,buf+pktposition);
		wrapper->timestamp(&pkt);
		break;
	}
	case 'S' :
	{
		sysevent_t pkt;
		set(&pkt,buf+pktposition);
		if ('Q'==pkt.code)
		{
			msgcount = 0;
			starttime = gettime();
		}
		wrapper->sysevent(&pkt);
		break;
	}
	case 'R' :
	{
		stkdirectory_t pkt;
		set(&pkt,buf+pktposition);
		wrapper->stkdirectory(&pkt);
		break;
	}
	case 'H' :
	{
		stktradingaction_t pkt;
		set(&pkt,buf+pktposition);
		break;
	}
	case 'Y' :
	{
		regshorestriction_t pkt;
		set(&pkt,buf+pktposition);
		break;
	}
	case 'L' :
	{
		mktparticipantposition_t pkt;
		set(&pkt,buf+pktposition);
		break;
	}
	case 'A' :
	{
		addorder_t pkt;
		set(&pkt,buf+pktposition);
		wrapper->addorder(&pkt);
		break;
	}
	case 'F' :
	{
		addordermpid_t pkt;
		set(&pkt,buf+pktposition);
		wrapper->addordermpid(&pkt);
		break;
	}
	case 'E' :
	{
		orderexecuted_t pkt;
		set(&pkt,buf+pktposition);
		wrapper->executeorder(&pkt);
		break;
	}
	case 'C' :
	{
		orderexecutedwithprice_t pkt;
		set(&pkt,buf+pktposition);
		wrapper->executeorderwithprice(&pkt);
		break;
	}
	case 'X' :
	{
		orderreduce_t pkt;
		set(&pkt,buf+pktposition);
		wrapper->reduceorder(&pkt);
		break;
	}
	case 'D' :
	{
		orderdelete_t pkt;
		set(&pkt,buf+pktposition);
		wrapper->deleteorder(&pkt);
		break;
	}
	case 'U' :
	{
		orderreplace_t pkt;
		set(&pkt,buf+pktposition);
		wrapper->replaceorder(&pkt);
		break;
	}
	case 'P' :
	{
		trade_t pkt;
		set(&pkt,buf+pktposition);
		break;
	}
	case 'Q' :
	{
		crosstrade_t pkt;
		set(&pkt,buf+pktposition);
		break;
	}
	case 'B' :
	{
		brokentrade_t pkt;
		set(&pkt,buf+pktposition);
		break;
	}
	case 'I' :
	{
		noii_t pkt;
		set(&pkt,buf+pktposition);
		break;
	}
	case 'G' :
	{
		rpii_t pkt;
		set(&pkt,buf+pktposition);
		break;
	}
	default :
	{
		printf("unrecognized header '%c'\n",header);
		printf("pktposition:%d\n",pktposition);
		header = 0;
		break;
	}
	}
	//printf("header %c\n",buf[0]);
	if (usebuffer) pktposition += pktsize;
	msgcount++;
	return header;
}

void itch41reader::bufferedread(int fd)
{
	//printf("buf:%d,position:%d\n",buf,buf+pktposition);
	memcpy(buf,(void*)(buf+pktposition),bufsize-pktposition); // shift them all over
	int bytes = read(fd,(void*)(buf+bufsize-pktposition),pktposition); // read from offset
	bytesread += bytes;
	if (bytes < pktposition)
	{
		// set it all to zero so everything will stop (and next header will be zero)
		memset((void*)(buf+bufsize-(pktposition-bytes)),0,pktposition-bytes);
	}
	pktposition = 0;
}

